package com.google.code.sysarch.v1.e34;

import java.util.Vector;
import com.google.code.sysarch.common.util.Parameter;

public class Scheduler extends Thread {

	public static final int MIN_TIMESLICE = 20;
	protected Vector<iThread> interactiveThreadsBuffer;	// queues for threads
	protected Vector<iThread> balancedThreadsBuffer;
	protected Vector<iThread> computationallyIntensiveThreadsBuffer;
	int defaultTimeslice;

	
	// scheduler runs the workers
	Scheduler(Parameter p, v1_34 GUI) {
		int numThreads;		// variables used to start threads
		int work;
		int timeslice;
		iThread thread;
		
		// parse parameter input
		numThreads = p.getInt("n", v1_34.NUM_MAX_THREADS/2, 1, v1_34.NUM_MAX_THREADS);	// read parameters
		defaultTimeslice = p.getInt("t", 3, 1, 100);
		int defaultWork = p.getInt("w", 1000, 10, 50000);

		// initialize queues
		interactiveThreadsBuffer = new Vector<iThread>(numThreads);
		balancedThreadsBuffer = new Vector<iThread>(numThreads);
		computationallyIntensiveThreadsBuffer = new Vector<iThread>(numThreads);

		 // assign scheduler to class iThread
		iThread.myScheduler = this;

		// start threads
		for (int i=0; i<numThreads; i++) {
			work = p.getInt("w"+i, defaultWork, 10, 50000);			
			thread = new iThread(work);
			thread.setPriority(Thread.MIN_PRIORITY);
			thread.setProgressBar(GUI.getBar());
			thread.defineAsInteractive();	// threads always start as interactive in v1_34			
			timeslice = p.getInt("t"+i, defaultTimeslice, 1, 100);
			thread.setDefaultTimeslice(timeslice*MIN_TIMESLICE);
			
			thread.start();
		}
	}
	
	
	// scheduler wakes up one thread, then sleeps for the duration of its timeslice
	public void run() {
		iThread runningThread = null;
		
		while (true) {	// this is our main loop
			while ((runningThread = dequeue()) == null) {
				yield();
			}
			synchronized (runningThread) {
				runningThread.setRunning(true);				
				runningThread.notify();
			}
			try {
				sleep(runningThread.getTimeslice());
			} catch (InterruptedException e) { }
			runningThread.setRunning(false);
			
			// wait until running thread is sleeping
			synchronized(runningThread) { }
		}
	}
	
	
	// adds a thread to one of the three queues
	public void queue(iThread thread) {
		switch (thread.ThreadType) {
		case 0: // interactive
			if (!interactiveThreadsBuffer.contains(thread)) {
				thread.setColor(iThread.INTERACTIVE_THREAD_COLOR);
				thread.setTimeslice(thread.getDefaultTimeslice());
				interactiveThreadsBuffer.addElement(thread);
			}
			break;
		case 1: // balanced
			if (!balancedThreadsBuffer.contains(thread)) {
				thread.setColor(iThread.BALANCED_THREAD_COLOR);
				thread.setTimeslice(2*thread.getDefaultTimeslice());
				balancedThreadsBuffer.addElement(thread);
			}
			break;
		case 2: // computationally intensive
			if (!computationallyIntensiveThreadsBuffer.contains(thread)) {
				thread.setColor(iThread.COMPUTATIONALLYINTENSIVE_THREAD_COLOR);
				thread.setTimeslice(4*thread.getDefaultTimeslice());
				computationallyIntensiveThreadsBuffer.addElement(thread);
			}
			break;
		default:
			if (!interactiveThreadsBuffer.contains(thread)) {
				thread.setColor(iThread.INTERACTIVE_THREAD_COLOR);
				thread.setTimeslice(thread.getDefaultTimeslice());
				interactiveThreadsBuffer.addElement(thread);
			}
			break;
		}
	}
	
	
	// searches for the next thread to execute and removes it from the queue
	public iThread dequeue() throws ArrayIndexOutOfBoundsException {
		iThread thread = null;
		
		// search for thread in this order: interaktive => ausgeglichene => rechenintensive
		if ((thread == null) && (interactiveThreadsBuffer.size() > 0)) thread = interactiveThreadsBuffer.remove(0);
		if ((thread == null) && (balancedThreadsBuffer.size() > 0)) thread = balancedThreadsBuffer.remove(0);
		if ((thread == null) && (computationallyIntensiveThreadsBuffer.size() > 0)) thread = computationallyIntensiveThreadsBuffer.remove(0);

		return thread;
	}

	
}